package com.zjmzxfzhl.integrityconstraint.annotation;

import cn.hutool.extra.spring.SpringUtil;
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import io.github.resilience4j.core.lang.Nullable;
import io.github.resilience4j.spelresolver.SpelResolver;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Method;

/**
 * SpareImplAnnotation注解的AOP切面处理
 *
 * @author 庄金明
 */
@Aspect
@Slf4j
public class SpareImplAnnotationAspect {

    private final CircuitBreakerRegistry circuitBreakerRegistry;
    private final SpelResolver spelResolver;

    public SpareImplAnnotationAspect(CircuitBreakerRegistry circuitBreakerRegistry, SpelResolver spelResolver) {
        this.circuitBreakerRegistry = circuitBreakerRegistry;
        this.spelResolver = spelResolver;
    }

    @Pointcut(value = "@within(spareService) || @annotation(spareService)", argNames = "spareService")
    public void matchAnnotatedClassOrMethod(SpareImplAnnotation spareImplAnnotation) {
    }

    @Around(value = "matchAnnotatedClassOrMethod(spareService)", argNames = "proceedingJoinPoint, spareService")
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint,
                               @Nullable SpareImplAnnotation spareImplAnnotation) throws Throwable {

        if (spareImplAnnotation == null) {
            spareImplAnnotation = AnnotationUtils.findAnnotation(proceedingJoinPoint.getTarget().getClass(), SpareImplAnnotation.class);
        }

        if (spareImplAnnotation == null) {
            return proceedingJoinPoint.proceed();
        }
        Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
        String config = spelResolver.resolve(method, proceedingJoinPoint.getArgs(), spareImplAnnotation.config());
        CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker(config, config);
        CircuitBreaker.State state = circuitBreaker.getState();
        if (state.equals(CircuitBreaker.State.CLOSED) || state.equals(CircuitBreaker.State.HALF_OPEN)) {
            return circuitBreaker.decorateCheckedSupplier(proceedingJoinPoint::proceed).apply();
        }

        // 微服务场景可以根据配置手动设置redis不可用
        // String redisEnabled = StrUtil.blankToDefault(SpringUtil.getProperty("redis.enabled"), "true");
        // if (Boolean.valueOf(redisEnabled)) {
        //     return proceedingJoinPoint.proceed();
        // }

        String spareBeanId = spelResolver.resolve(method, proceedingJoinPoint.getArgs(), spareImplAnnotation.spareBeanId());
        Object service = SpringUtil.getBean(spareBeanId);
        Method realMethod = service.getClass().getMethod(method.getName(), method.getParameterTypes());
        return realMethod.invoke(service, proceedingJoinPoint.getArgs());
    }

}
